#10.1-从文件中读取数据

#使用open()函数打开文件时，需要提供文件的路径和文件名。
#使用with关键字时，open()返回的文件对象只在with代码块内可用。
#with的工作原理：
#1.创建一个文件对象，将其赋值给变量file_object。
#2.调用open()函数时，Python将尝试打开文件，并将其存储在变量file_object中。
#3.在with代码块内，Python将执行文件的操作。
#4.在with代码块外，Python将关闭文件。
#5.在with代码块内，Python将执行文件的操作。
with open ('Python-Crash-Course\src\pi_digits.txt') as file_object:
    contents = file_object.read() #read函数在文件尾会返回一个空字符串
    print(contents.rstrip()) #使用rstrip()删除文件末尾多余的空行

#10.1.2逐行读取
#使用for循环逐行读取文件
filename = 'Python-Crash-Course\src\pi_digits.txt'
with open(filename) as file_object:
    for line in file_object:
        print(line.rstrip())

#10.1.3创建一个包含文件各行内容的列表
#使用readlines()函数从文件中读取每一行，并将其存储在一个列表中。
filename = 'Python-Crash-Course\src\pi_digits.txt'
with open(filename) as file_object:
    lines = file_object.readlines() #readlines()函数从文件中读取每一行，并将其存储在一个列表中。
for line in lines:
    print(line.rstrip())

#10.1.4使用文件的内容
#使用文件的内容
filename = 'Python-Crash-Course\src\pi_digits.txt'
with open(filename) as file_object:
    lines = file_object.readlines() #readlines()函数从文件中读取每一行，并将其存储在一个列表中。
pi_string = '' #创建一个空字符串，用于存储文件中的内容。
for line in lines:
    pi_string += line.strip()
print(pi_string)
print(len(pi_string)) #使用len()函数计算文件中的字符数  。

#10.1.5包含一百万位的大型文件
#使用文件的内容
filename = 'Python-Crash-Course\src\pi_million_digits.txt'
with open(filename) as file_object:
    lines = file_object.readlines() #readlines()函数从文件中读取每一行，并将其存储在一个列表中。

pi_string = '' #创建一个空字符串，用于存储文件中的内容。
for line in lines:
    pi_string += line.strip() #使用strip()函数删除字符串中的空格和换行符。
print(f"{pi_string[:52]}...") #使用切片操作符[:52]，只打印字符串中的前52个字符。
print(len(pi_string)) #使用len()函数计算文件中的字符数  

#10.1.6圆周率值中包含你的生日吗
brithday = input("Enter your birthday, in the form mmddyy: ") #使用input()函数提示用户输入生日。
if brithday in pi_string: #使用in运算符检查生日是否在字符串中。
    print("Your birthday appears in the first million digits of pi!") #如果生日在字符串中，打印一条消息。
else: #如果生日不在字符串中，打印另一条消息。
    print("Your birthday does not appear in the first million digits of pi.")


#10.2写入文件
#使用open()函数打开文件时，需要提供文件的路径和文件名，以及打开文件的模式。
#以写入模式打开文件要小心，因为如果文件存在，Python将在返回文件对象前清空该文件。
with open('Python-Crash-Course\src\programming.txt', 'w') as file_object: 
    file_object.write("I love programming.\n") #使用write()函数将字符串写入文件。
    file_object.write("I love creating new games.\n") #使用write()函数将字符串写入文件。

#10.2.2写入多行
#附加模式：如果要在文件末尾添加内容，而不是覆盖原有的内容，可以使用附加模式。
with open('Python-Crash-Course\src\programming.txt', 'a') as file_object: #使用附加模式打开文件。
    file_object.write("I also love finding meaning in large datasets.\n") #使用write()函数将字符串写入文件。
    file_object.write("I love creating apps that can run in a browser.\n") #使用write()函数将字符串写入文件。

#练习
#10-1
usename = input('请输入姓名：')
while True:
    if usename != 'quit':
       with open('Python-Crash-Course\src\guest.txt', 'a') as file_object:
            file_object.write(usename)
    elif usename == 'quit':
        break
    usename = input('再次输入或输入quit退出')


#10.3异常：
#try-except的作用是，当try中的代码块出现异常时，执行except中的代码块，而不会终止程序。
#不出现异常时，执行try中的代码块。
#try-except-else的作用是，当try中的代码块出现异常时，执行except中的代码块，而不会终止程序。

#10.3.1处理ZeroDivisionError异常
#print(5/0) #ZeroDivisionError: division by zero  除数为0，会出现异常。
try:
    print(5/0) #使用try-except代码块，当try中的代码块出现异常时，执行except中的代码块，而不会终止程序。
except ZeroDivisionError: #使用except语句，告诉Python，如果发生了ZeroDivisionError异常，就执行except中的代码块。
    print("You can't divide by zero!") #使用print()函数打印一条消息。

#10.3.2使用异常避免崩溃
print("Give me two numbers, and I'll divide them.") #使用print()函数打印一条消息。
print("Enter 'q' to quit.") #使用print()函数打印一条消息。
while True: #使用while循环，让用户输入两个数字。
    first_number = input("\nFirst number: ") #使用input()函数提示用户输入第一个数字。
    if first_number == 'q': #使用if语句，判断用户是否输入了'q'。
        break #使用break语句，退出循环。
    second_number = input("Second number: ") #使用input()函数提示用户输入第二个数字。
    if second_number == 'q': #使用if语句，判断用户是否输入了'q'。
        break #使用break语句，退出循环。
    try: #使用try-except代码块，当try中的代码块出现异常时，执行except中的代码块，而不会终止程序。
        answer = int(first_number) / int(second_number) #使用int()函数将字符串转换为整数。
    except ZeroDivisionError: #使用except语句，告诉Python，如果发生了ZeroDivisionError异常，就执行except中的代码块。
        print("You can't divide by zero!") #使用print()函数打印一条消息。
    else: #使用else语句，告诉Python，如果try中的代码块没有出现异常，就执行else中的代码块。
        print(answer) #使用print()函数打印结果。

#处理FileNotFoundError异常
filename = 'alice.txt' #使用变量filename存储文件名。
try: #使用try-except代码块，当try中的代码块出现异常时，执行except中的代码块，而不会终止程序。
    with open(filename) as f_obj: #使用open()函数打开文件。
        contents = f_obj.read() #使用read()函数读取文件中的内容。
except FileNotFoundError: #使用except语句，告诉Python，如果发生了FileNotFoundError异常，就执行except中的代码块。
    msg = "Sorry, the file " + filename + " does not exist." #使用print()函数打印一条消息。
    print(msg) #使用print()函数打印一条消息。
else: #使用else语句，告诉Python，如果try中的代码块没有出现异常，就执行else中的代码块。
    #计算文件中出现多少次单词'rabbit'。
    words = contents.split() #使用split()函数将字符串转换为列表。
    num_words = len(words) #使用len()函数计算列表中的元素个数。


#10.3.3处理多个文件
def count_words(filename): #定义一个函数，用于计算文件中出现多少次单词'rabbit'。
    """计算一个文件大致包含多少个单词""" #使用docstring文档字符串，描述函数的功能。
    try: #使用try-except代码块，当try中的代码块出现异常时，执行except中的代码块，而不会终止程序。
        with open(filename) as f_obj: #使用open()函数打开文件。
            contents = f_obj.read() #使用read()函数读取文件中的内容。
    except FileNotFoundError: #使用except语句，告诉Python，如果发生了FileNotFoundError异常，就执行except中的代码块。
        msg = "Sorry, the file " + filename + " does not exist." #使用print()函数打印一条消息。
        print(msg) #使用print()函数打印一条消息。
    else: #使用else语句，告诉Python，如果try中的代码块没有出现异常，就执行else中的代码块。
        #计算文件中出现多少次单词'rabbit'。
        words = contents.split() #使用split()函数将字符串转换为列表。
        num_words = len(words) #使用len()函数计算列表中的元素个数。
        print("The file " + filename + " has about " + str(num_words) + " words.") #使用print()函数打印一条消息。

filenames = ['alice.txt', 'siddhartha.txt', 'moby_dick.txt', 'little_women.txt'] #使用变量filenames存储文件名。

for filename in filenames: #使用for循环，遍历文件名列表。
    count_words(filename) #调用函数count_words()，计算文件中出现多少次单词'rabbit'。

#存储数据
#什么是JSON？
#JSON（JavaScript Object Notation）是一种轻量级的数据交换格式，易于人阅读和编写，同时也易于机器解析和生成。
#json格式的数据由键值对组成，键和值之间用冒号分隔，键值对之间用逗号分隔，整个数据用花括号括起来。

#使用json.dump()和json.load()
#使用json.dump()和json.load()可以将Python对象转换为JSON格式的数据，并将其保存到文件中，
# 或者从文件中读取JSON格式的数据，并将其转换为Python对象。
import json #导入json模块。
numbers = [2, 3, 5, 7, 11, 13] #使用变量numbers存储一个列表。
filename = 'Python-Crash-Course\src\\numbers.json' #使用变量filename存储文件名。
with open(filename, 'w') as f_obj: #使用open()函数打开文件。
    json.dump(numbers, f_obj) #使用json.dump()函数将列表转换为JSON格式的数据，并将其保存到文件中。

#使用json.load()函数从文件中读取JSON格式的数据，并将其转换为Python对象。
with open(filename) as f_obj: #使用open()函数打开文件。
    numbers = json.load(f_obj) #使用json.load()函数从文件中读取JSON格式的数据，并将其转换为Python对象。

print(numbers) #使用print()函数打印列表。

#保存和读取用户生成的数据
filename_ = 'Python-Crash-Course\src\username.json' #使用变量filename存储文件名。
try: #使用try-except代码块，当try中的代码块出现异常时，执行except中的代码块，而不会终止程序。
    with open(filename_) as f_obj: #使用open()函数打开文件。 
        username = json.load(f_obj) #使用json.load()函数从文件中读取JSON格式的数据，并将其转换为Python对象。
except FileNotFoundError: #使用except语句，告诉Python，如果发生了FileNotFoundError异常，就执行except中的代码块。
    username = input("What is your name? ") #使用input()函数提示用户输入姓名。
    with open(filename_, 'w') as f_obj: #使用open()函数打开文件。
        json.dump(username, f_obj) #使用json.dump()函数将姓名转换为JSON格式的数据，并将其保存到文件中。
        print("We'll remember you when you come back, " + username + "!") #使用print()函数打印一条消息。
else: #使用else语句，告诉Python，如果try中的代码块没有出现异常，就执行else中的代码块。
    print("Welcome back, " + username + "!") #使用print()函数打印一条消息。

#重构
#重构是对代码的改进，使其更清晰、更简洁、更易于理解和维护。
#将上述代码功能分解为多个代码块，每个代码块实现一个功能，再通过函数调用实现功能的组合。
def get_stored_username(): #定义一个函数，用于从文件中读取JSON格式的数据，并将其转换为Python对象。
    """如果存储了用户名，就获取它""" #使用docstring文档字符串，描述函数的功能。
    filename_ = 'Python-Crash-Course\src\username.json' #使用变量filename存储文件名。
    try: #使用try-except代码块，当try中的代码块出现异常时，执行except中的代码块，而不会终止程序。
        with open(filename_) as f_obj: #使用open()函数打开文件。
            username = json.load(f_obj) #使用json.load()函数从文件中读取JSON格式的数据，并将其转换为Python对象。
    except FileNotFoundError: #使用except语句，告诉Python，如果发生了FileNotFoundError异常，就执行except中的代码块。
        return None #使用return语句返回None。
    else: #使用else语句，告诉Python，如果try中的代码块没有出现异常，就执行else中的代码块。
        return username #使用return语句返回Python对象。

def get_new_username(): #定义一个函数，用于提示用户输入姓名，并将其转换为JSON格式的数据，并将其保存到文件中。
    """提示用户输入用户名""" #使用docstring文档字符串，描述函数的功能。
    username = input("What is your name? ") #使用input()函数提示用户输入姓名。
    filename_ = 'Python-Crash-Course\src\username.json' #使用变量filename存储文件名。
    with open(filename_, 'w') as f_obj: #使用open()函数打开文件。
        json.dump(username, f_obj) #使用json.dump()函数将姓名转换为JSON格式的数据，并将其保存到文件中。
    return username #使用return语句返回Python对象。

def greet_user(): #定义一个函数，用于问候用户。
    """问候用户，并指出其名字""" #使用docstring文档字符串，描述函数的功能。
    username = get_stored_username() #调用函数get_stored_username()，获取用户名。
    if username: #使用if语句，判断用户名是否存在。
        print("Welcome back, " + username + "!") #使用print()函数打印一条消息。
    else: #使用else语句，告诉Python，如果if中的代码块没有执行，就执行else中的代码块。
        username = get_new_username() #调用函数get_new_username()，获取用户名。
        print("We'll remember you when you come back, " + username + "!") #使用print()函数打印一条消息。

greet_user() #调用函数greet_user()，问候用户。
